﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Serialization;

namespace GameLogic.Controls
{
    [TemplatePart(Name = C_HighscoresListBoxName, Type = typeof(ListBox))]
    [TemplatePart(Name = C_NewEntryPanelName, Type = typeof(StackPanel))]
    [TemplatePart(Name = C_RankTextBlockName, Type = typeof(TextBlock))]
    [TemplatePart(Name = C_PlayerTextBoxName, Type = typeof(TextBox))]
    [TemplatePart(Name = C_ContinueButtonName, Type = typeof(Button))]
    public class HighscoreControl : Control
    {
        const string C_HighscoresListBoxName = "_highscoresListBox"; // Name of the ListBox displaying the highscores
        const string C_NewEntryPanelName = "_newEntryPanel";
        const string C_RankTextBlockName = "_rankTextBlock";
        const string C_PlayerTextBoxName = "_playerTextBox";
        const string C_ContinueButtonName = "_continueButton";

        ListBox highscoresListBox; // ListBox for displaying the highscores
        StackPanel newEntryPanel;
        TextBlock rankTextBlock;
        TextBox playerTextBox;
        Button continueButton;
        //int currentScore;

        List<HighscoreEntry> highscoreList;

        //public enum HighscoreStates
        //{
        //    Show,
        //    NewEntry
        //}

        #region Dependency Properties

        [Category("Highscores")]
        public int Range
        {
            get { return (int)GetValue(RangeProperty); }
            set { SetValue(RangeProperty, value); }
        }

        public static readonly DependencyProperty RangeProperty =
            DependencyProperty.Register(
                "Range",
                typeof(int),
                typeof(HighscoreControl),
                new PropertyMetadata(10, null));

        // Name of the XML file storing the highscores
        [Category("Highscores")]
        public string FileName
        {
            get { return (string)GetValue(FileNameProperty); }
            set { SetValue(FileNameProperty, value); }
        }

        public static readonly DependencyProperty FileNameProperty =
            DependencyProperty.Register(
                "FileName", typeof(string),
                typeof(HighscoreControl),
                new PropertyMetadata("highscores.xml", null));

        #endregion

        public HighscoreControl()
        {
            this.DefaultStyleKey = typeof(HighscoreControl);

            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            this.Loaded += (s, e) =>
            {
                CheckForEntry(GameContainer.LastPoints);
            };

            highscoreList = new List<HighscoreEntry>();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Get template children
            highscoresListBox = GetTemplateChild(C_HighscoresListBoxName) as ListBox;
            newEntryPanel = GetTemplateChild(C_NewEntryPanelName) as StackPanel;
            playerTextBox = GetTemplateChild(C_PlayerTextBoxName) as TextBox;
            rankTextBlock = GetTemplateChild(C_RankTextBlockName) as TextBlock;
            continueButton = GetTemplateChild(C_ContinueButtonName) as Button;

            continueButton.Click += (s, e) =>
                {
                    SaveCurrentEntry();
                }; 

            playerTextBox.Text = GameContainer.LastUserName;

            playerTextBox.KeyUp += (s, e) =>
                {
                    if (e.Key == System.Windows.Input.Key.Enter)
                    {
                        SaveCurrentEntry();
                    }
                };

            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            this.Load(); 
            this.UpdateDisplay(); 
        }

        public void CheckForEntry(int score)
        {
            if (score <= 0)
            {
                newEntryPanel.Visibility = System.Windows.Visibility.Collapsed;
                return;
            }

            //currentScore = score;
            int currentRank = this.GetRank(score);

            // Check if given score is good enough for entry
            if (currentRank > 0 && currentRank <= Range)
            {
                newEntryPanel.Visibility = System.Windows.Visibility.Visible;

                rankTextBlock = GetTemplateChild(C_RankTextBlockName) as TextBlock;
                if (rankTextBlock != null)
                {
                    rankTextBlock.Text = currentRank.ToString();
                }
            }
            else
            {
                newEntryPanel.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void SaveCurrentEntry()
        {
            GameContainer.LastUserName = playerTextBox.Text;

            // Insert player name in highscore list
            this.Insert(playerTextBox.Text, GameContainer.LastPoints);

            // Refresh list
            this.UpdateDisplay();

            newEntryPanel.Visibility = System.Windows.Visibility.Collapsed;

            GameContainer.LastPoints = -1;
        }

        public int GetRank(int score)
        {
            SortList();

            // If highscores are empty...
            if (highscoreList.Count == 0)
            {
                return 1;
            }

            // Loop trough all entries in highscores
            for (int i = 0; i < highscoreList.Count; i++)
            {
                // If score is high enough to be entered into highscores...
                if (highscoreList[i].Score < score)
                {
                    return i + 1; // .. return the rank
                }
            }

            return highscoreList.Count + 1;
        }

        private void SortList()
        {
            // Sort List
            var highscoreHelper = from h in highscoreList
                                  orderby h.Score descending 
                                  select h;
            highscoreList = highscoreHelper.ToList();
        }

        // Insert player and score into highscores
        private void Insert(string player, int score)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            HighscoreEntry ne = new HighscoreEntry();
            ne.Player = player;
            ne.Score = score;
            ne.Date = DateTime.Now;

            this.AddEntry(ne);
        }

        // Add entry to highscores
        void AddEntry(HighscoreEntry entry)
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            // Load highscores
            this.Load();

            highscoreList.Add(entry); // Add entry
            SortList();

            // Remove lowest scores, if required
            if (highscoreList.Count > this.Range)
            {
                highscoreList.RemoveRange(this.Range, highscoreList.Count - this.Range);
            }

            this.UpdateDisplay(); // TODO: Remove, just for testing (DS)

            // Save highscores
            this.Save();
        }

        // TODO: Databinding für die Liste verwenden und nicht manuell dort einfügen.

        // Display highscores in ListBox
        void UpdateDisplay()
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            SortList();

            // Only update if visible/existing
            if (highscoresListBox != null)
            {
                // Clear ListBox
                highscoresListBox.Items.Clear();

                // Loop trough all entries in highscores
                for (int i = 0; i < highscoreList.Count; i++)
                {
                    // Add entry to ListBox
                    ListBoxItem lbi = new ListBoxItem();
                    lbi.Content = String.Format(
                        "{0} - {1} - {2} - {3}",
                        i + 1,
                        highscoreList[i].Player,
                        highscoreList[i].Score,
                        highscoreList[i].Date);

                    highscoresListBox.Items.Add(lbi);
                }
                UpdateLayout();
            }
        }

        // Load Highscores
        void Load()
        {
            if (DesignerProperties.IsInDesignTool)
                return; 

            using (IsolatedStorageFile isoStore =
                IsolatedStorageFile.GetUserStoreForApplication())
            {
                // If file does not exist, create a new one with dummy entries
                if (!isoStore.FileExists(this.FileName))
                {
                    // TODO: Create dummy highscores
                }
                else
                {
                    // Open file
                    using (IsolatedStorageFileStream isoStream =
                        new IsolatedStorageFileStream(this.FileName, FileMode.Open, isoStore))
                    {
                        TextReader reader = null;

                        try
                        {
                            // Create an instance of the XmlSerializer class;
                            // specify the type of object to be deserialized.
                            XmlSerializer serializer = new XmlSerializer(typeof(List<HighscoreEntry>));
                            reader =  new StreamReader(isoStream);
                      
                            // Use the Deserialize method to restore the object's state with
                            // data from the XML document.
                            highscoreList = (List<HighscoreEntry>)serializer.Deserialize(isoStream);
                            reader.Close();
                        }
                        catch
                        {
                        }
                        finally
                        {
                            if (reader != null)
                                reader.Dispose();
                        }
                        isoStream.Close();
                    }
                }
            }
        }

        /// <summary>
        /// Saves Highscores
        /// </summary>
        void Save()
        {
            if (DesignerProperties.IsInDesignTool)
            {
                return;
            }

            SortList();

            using (IsolatedStorageFile isoStore =
                 IsolatedStorageFile.GetUserStoreForApplication())
            {
                // Create new file
                using (IsolatedStorageFileStream isoStream =
                    new IsolatedStorageFileStream(this.FileName, FileMode.Create, isoStore))
                {
                    TextWriter writer = null;
                    try
                    {
                        // Create an instance of the XmlSerializer class;
                        // specify the type of object to serialize.
                        XmlSerializer serializer = new XmlSerializer(typeof(List<HighscoreEntry>));
                        writer = new StreamWriter(isoStream);
                  
                        // Serialize the highscores, and close the TextWriter.
                        serializer.Serialize(writer, highscoreList);
                        writer.Close();
                    }
                    catch
                    {
                    }
                    finally
                    {
                        if (writer != null)
                            writer.Dispose();                
                    }
                    isoStream.Close();
                }
            }
        }
    }
}
